--[[
#*************************************************************
#  Copyright (c) 2003-2013, Emerging Threats
#  All rights reserved.
#  
#  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
#  following conditions are met:
#  
#  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following 
#    disclaimer.
#  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 
#    following disclaimer in the documentation and/or other materials provided with the distribution.
#  * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived 
#    from this software without specific prior written permission.
#  
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, 
#  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
#  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
#
#*************************************************************

Detection for suspicious Silverlight files requires lua zip module.
sudo apt-get install liblua5.1-zip-dev 

This lua script can be run standalone and verbosely on a Silverlight file with
echo "run()" | lua -i <script name> <Silverlight Package>

Chris Wakelin
Will Metcalf
--]]

require("zip")
-- {strings to match, number of matching strings needed, simple strings, description}
susp_class = {
              {"WriteableBitmap","get_PixelWidth","get_PixelHeight","SetSource","\137\080\078\071\013\010\026\010\000\000\000\013\073\072\068\082\000\000\000\008\000\000\000\004\008\003\000\000\000\132\019\142\194",5,true,"CVE-2013-0074 VariousEK's",0},
              {"F:\\EXP\\",".pdb",2,true,"CVE-2013-0074 Nuclear",0},
              {"d%z?r%z?o%z?p%z?B%z?o%z?m%z?b","W%z?S%z?c%z?r%z?i%z?p%z?t%z?%.%z?S%z?h%z?e%z?l%z?l",1,false,"CVE-2013-0074 Unknown 18cbe962a6d290543d47a51a0837d7bb",0},
              {"XPerTu x64 productions 2009","Object","System.Windows.Browser","AesManaged",4,true,"CVE-2013-0074 AnglerEK",0},
              {"\009\032\069\071\071\033\158",1,true,"Magnitude EK",0},
              {"GetChars","GetDecoder","GetCharCount","ExecPayload","Shell32","Shell64","Assembly","System.Text",8,true,"CVE-2016-0034",0}
             }

obfus_strings = {
}
function init (args)
    local needs = {}
    needs["http.response_body"] = tostring(true)
    return needs
end

function match_strings(a,match_set,verbose)
    local rtn = 0
    local n,m
    local num_strings = #match_set - 4 
    
    local match_num = match_set[num_strings+1]
    local plain = match_set[num_strings+2]
    local desc = match_set[num_strings+3]
    local fnd
    
    for n = 1, num_strings, 1 do
        m = match_set[n]
        if m ~= 0 then
            fnd = string.find(a,m,1,plain)
            if fnd then
                match_set[num_strings+4] = match_set[num_strings+4] + 1
                if verbose == 1 then print("Found String " .. m .. " " .. match_set[num_strings+4] .. " of " .. match_num) end
                --match_set[num_strings+4] is our counter
                if match_set[num_strings+4] == match_num then
                    if verbose == 1 then print("Found " .. desc) end
                    match_set[n] = 0
                    rtn = 1
                    break
                else
                    --Found lets zero it out so we don't check again
                    match_set[n] = 0
                end
           end
        end
    end
    return rtn
end

function common(t,verbose)

    rtn = 0

    -- tmpfs setup should be faster
    -- mkdir -p /home/suricata/scratch  && sudo mount -t tmpfs -o size=1G,mode=0700 tmpfs /home/suricata/scratch && sudo chown suricata:suricata /home/suricata/scratch/
    -- tmpname = "/home/suricata/scratch/eviljars." .. tostring(os.time()) .. "." .. tostring(math.random(2000000,9000000))
    tmpname = os.tmpname()

    tmp = io.open(tmpname,'w')
    tmp:write(t)
    tmp:close()

    z = zip.open(tmpname)

    if z then 
        for w in z:files() do
            f = z:open(w.filename);
            u = f:read("*all")
            f:close()
            if (verbose==1) then print("Checking " .. w.filename) end
            if (string.sub(u,1,2) == "MZ") then
                for l,s in pairs(susp_class) do
                    if (verbose==1) then print("Looking for " .. s[#s-1] .. " in ".. w.filename) end
                    if match_strings(u,s,verbose) == 1 then
                        rtn = 1
                        if (verbose == 0) then
                           break
                        end
                    end
                end
            end
    end
    z:close()
    end
    os.remove(tmpname)
    return rtn
end

-- return match via table
function match(args)
    local t = tostring(args["http.response_body"])
    return common(t,0)
end

function run()
  local f = io.open(arg[1])
  local t = f:read("*all")
  f:close()
  if common(t,1) == 1 then print("Found Suspicious String in " .. arg[1]) end
end

